import adapter from "webrtc-adapter";
import Camera, { getUserMedia } from "camera-utils";
import { isMobile, getElement, mergeDeep, createElement, getMimeType } from "./utils";
import { css } from "petty-css-js";

export { getUserMedia };

export const MediaIdeal = {
  width: 480,
  height: 640,
}; // 摄像头分辨率

export const defaultOptions = {};

export interface OptionsType {
  className?: string;
  onStart?: () => void;
  onPause?: () => void;
  onResume?: () => void;
  onStop?: (blob: Blob) => void;
  onDataavailable?: (event: BlobEvent) => void;
  onError?: (err: Event) => void;
}

export class Recorder {
  camera!: Camera;
  element!: HTMLElement;
  options!: OptionsType;
  video!: HTMLVideoElement;
  canvas!: HTMLCanvasElement;
  container!: HTMLDivElement;
  mediaRecorder!: MediaRecorder;
  recordedChunks!: Blob[];
  mimeType!: string;
  frameRate!: number;
  constructor(el: HTMLElement | string, userOptions?: OptionsType) {
    if (!isMobile()) {
      console.warn("Device must be Mobile");
      return;
    }

    if (!el) {
      console.warn("Element cannot be empty");
      return;
    }
    if (window.MediaRecorder === undefined) {
      console.warn(`MediaRecorder not supported on your ${adapter.browserDetails.browser} browser, use the latest version of Firefox or Chrome`);
      return;
    }
    this.element = getElement(el);
    this.options = mergeDeep(defaultOptions, userOptions);
    this.init();
  }
  async init() {
    // init elements
    await this.initElements();

    // new camera
    this.hideVideo();
    this.camera = new Camera(
      {
        width: MediaIdeal.width,
        height: MediaIdeal.height,
        facingMode: "user",
      },
      this.video,
      this.canvas,
    );
    this.camera.start();
    this.showVideo();
  }
  async initElements() {
    const { className = "" } = this.options;

    // container
    this.container = createElement(
      "div",
      css(
        "display: flex;align-items: center;justify-content: center;height: 100%; width: 100%;transform: translate3d(0, 0, 0);",
        className ? `${className}-container` : "",
      ),
    ) as HTMLDivElement;

    // video
    this.video = createElement(
      "video",
      css(
        "display: none; width: 100%; height: 100%; transform: scaleX(-1);object-fit: fill;",
        className ? `${className}-video` : "",
      ),
    ) as HTMLVideoElement;

    // canvas
    this.canvas = createElement(
      "canvas",
      css(
        "width: 100%; height: 100%; position: absolute; top: 0px; left: 0px;z-index: -1;opacity: 0;transform: translate3d(0, 0, 0);",
        className ? `${className}-canvas` : "",
      ),
    ) as HTMLCanvasElement;

    if (this.video) {
      this.container.appendChild(this.video);
    }
    if (this.canvas) {
      this.container.appendChild(this.canvas);
    }
    if (this.container) {
      this.element.innerHTML = "";
      this.element.appendChild(this.container);
    }
  }
  async hideVideo() {
    if (this.video) {
      this.video.style.display = "none";
    }
  }
  async showVideo() {
    if (this.video) {
      this.video.style.display = "block";
    }
  }
  start() {
    try {
      // start camera
      const stream = this.video.srcObject as MediaStream;
      if (stream) {
        // frame rate
        if (stream.getVideoTracks()[0]) {
          const track = stream.getVideoTracks()[0]
          // 上报帧率
          if (track.getSettings().frameRate) {
            this.frameRate = track.getSettings().frameRate || 30;
          }
        }

        // mime type
        this.mimeType = getMimeType();
        if (this.mimeType) {
          this.mediaRecorder = new MediaRecorder(stream, { mimeType: this.mimeType });
        } else {
          this.mediaRecorder = new MediaRecorder(stream);
        }

        // clear recorded chunks
        this.recordedChunks = [];

        // onstart
        this.mediaRecorder.onstart = () => {
          if (this.options.onStart) {
            this.options.onStart()
          }
        }

        // onpause
        this.mediaRecorder.onpause = () => {
          if (this.options.onPause) {
            this.options.onPause()
          }
        }

        // onresume
        this.mediaRecorder.onresume = () => {
          if (this.options.onResume) {
            this.options.onResume()
          }
        }

        // onstop
        this.mediaRecorder.onstop = () => {
          if (this.options.onStop) {
            const blob = this.getBlobData();
            this.options.onStop(blob)
          }
        }

        // ondataavailable
        this.mediaRecorder.ondataavailable = event => {
          if (event.data.size > 0) {
            this.recordedChunks.push(event.data);
          }
          if (this.options.onDataavailable) {
            this.options.onDataavailable(event)
          }
        };

        // onerror
        this.mediaRecorder.onerror = (err) => {
          if (this.options.onError) {
            this.options.onError(err)
          }
        };

        // start
        this.mediaRecorder.start(this.frameRate);
      } else {
        console.error("navigator.getUserMedia error");
      }
    } catch (error) {
      console.error("Could not get local stream from mic/camera");
    }
  }
  pause() {
    if (this.mediaRecorder && this.mediaRecorder.state === 'recording') {
      this.mediaRecorder.pause();
    }
  }
  resume() {
    if (this.mediaRecorder && this.mediaRecorder.state === 'paused') {
      this.mediaRecorder.resume();
    }
  }
  stop() {
    if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
      this.mediaRecorder.stop();
    }
  }
  destroy() {
    this.recordedChunks = [];
    this.mediaRecorder.stop();
    this.camera.destory();
  }
  getBlobData() {
    return new Blob(this.recordedChunks, { type: this.mediaRecorder.mimeType });
  }
  getMimeType() {
    return this.mediaRecorder.mimeType;
  }
  getMediaRecorderState() {
    return this.mediaRecorder.state;
  }
  getMediaRecorder() {
    return this.mediaRecorder;
  }
  getCamera() {
    return this.camera;
  }
}

export default Recorder;
